{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "GAN_and_PBGAN.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "id": "P0ByywuhcZQW"
      },
      "source": [
        "# !nvidia-smi"
      ],
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6Dcj4YMNcoFh"
      },
      "source": [
        "**Initialize**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6ZTZWsEOcmZK"
      },
      "source": [
        "import tensorflow as tf\n",
        "import glob\n",
        "import imageio\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import os\n",
        "import PIL\n",
        "from tensorflow.keras import layers\n",
        "import time\n",
        "import scipy.io\n",
        "from sklearn.preprocessing import OneHotEncoder\n",
        "from random import random\n",
        "from IPython import display\n",
        "from scipy import signal\n",
        "\n",
        "tf.__version__\n",
        "\n",
        "read_file=scipy.io.loadmat('Subset_Biosec3.mat') # Load data"
      ],
      "execution_count": 2,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CCvyp5jMc_wg"
      },
      "source": [
        "**Set Target ID, GAN, PBGAN, Input**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MetRtWiqcmb_",
        "outputId": "e4d0c268-ea40-422c-b4e3-7f711d394b64",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "ID = 1 # Target ID\n",
        "GAN_model = 0 # GAN model. 0: DCGAN / 1: WGAN / 2: LSGAN\n",
        "PBGAN = 1 # PBGAN or PBGAN (W) ON/OFF. 0: ON / 1: OFF\n",
        "Data = 3 # Input. 0: DTW / 1: TP / 2: FP / 3: Cubic\n",
        "\n",
        "subject = np.arange(1,21).tolist()\n",
        "print('ID',ID)\n"
      ],
      "execution_count": 3,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "ID 1\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sN1fyHDHcmeq",
        "outputId": "f78f4add-336f-4b8b-d9d6-6b93882fe3a3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 298
        }
      },
      "source": [
        "train_data = np.array(read_file['GAN_data'])[subject.index(ID), Data] # Assign train data from only target\n",
        "train_data = np.reshape(train_data, [len(train_data),150,1,1])\n",
        "n_feature = train_data.shape[1]\n",
        "\n",
        "fig = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "ax.plot(train_data[0,:,0,0])\n",
        "plt.title('Data sample')"
      ],
      "execution_count": 4,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "Text(0.5, 1.0, 'Data sample')"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 4
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JPfI51CzfE3I"
      },
      "source": [
        "**Onehot Encoding and Data Shuffle**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SvtBolAvcmhh"
      },
      "source": [
        "enc = OneHotEncoder()\n",
        "\n",
        "randIndx = np.arange(train_data.shape[0])\n",
        "np.random.shuffle(randIndx)\n",
        "\n",
        "train_data = train_data[randIndx] # Random shuffle the train data\n",
        "train_data = train_data[:int(len(train_data) / 10) * 10] # Take most data for training\n",
        "\n",
        "train_labels = np.ones((len(train_data),1)) # Train label\n"
      ],
      "execution_count": 5,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-VECj40dcmkf"
      },
      "source": [
        "BUFFER_SIZE = 60000\n",
        "BATCH_SIZE = 10 # Mini-batch size\n",
        "\n",
        "# Shuffle with mini-batch\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(train_data).batch(BATCH_SIZE)\n",
        "labels = tf.data.Dataset.from_tensor_slices(train_labels).batch(BATCH_SIZE)"
      ],
      "execution_count": 6,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qt406f7tgM4X"
      },
      "source": [
        "**The Generator**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "kJBosH2Ocmna"
      },
      "source": [
        "def make_generator_model():\n",
        "    if (GAN_model == 0) or (GAN_model == 1): # DCGAN or WGAN\n",
        "      model = tf.keras.Sequential()\n",
        "      # FC Layer\n",
        "      model.add(layers.Dense(25*1*256, use_bias=False, input_shape=(200,)))\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Reshape((25, 1, 256)))\n",
        "      assert model.output_shape == (None, 25, 1, 256)\n",
        "      # Convolutional-Transpose Layer #1\n",
        "      model.add(layers.Conv2DTranspose(128, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 25, 1, 128)\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #2\n",
        "      model.add(layers.Conv2DTranspose(64, (5, 1), strides=(3, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 75, 1, 64)\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #3\n",
        "      model.add(layers.Conv2DTranspose(32, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 75, 1, 32)\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #4\n",
        "      model.add(layers.Conv2DTranspose(16, (5, 1), strides=(2, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 150, 1, 16)\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #5\n",
        "      model.add(layers.Conv2DTranspose(1, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      # Output\n",
        "      assert model.output_shape == (None, 150, 1, 1)\n",
        "\n",
        "      return model\n",
        "\n",
        "    elif GAN_model == 2: # LSGAN\n",
        "      model = tf.keras.Sequential()\n",
        "      # FC Layer\n",
        "      model.add(layers.Dense(25*1*256, use_bias=False, input_shape=(200,)))\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Reshape((25, 1, 256)))\n",
        "      assert model.output_shape == (None, 25, 1, 256) \n",
        "      # Convolutional-Transpose Layer #1\n",
        "      model.add(layers.Conv2DTranspose(128, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 25, 1, 128)\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #2\n",
        "      model.add(layers.Conv2DTranspose(64, (5, 1), strides=(3, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 75, 1, 64)\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #3\n",
        "      model.add(layers.Conv2DTranspose(32, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 75, 1, 32)\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #4\n",
        "      model.add(layers.Conv2DTranspose(16, (5, 1), strides=(2, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      assert model.output_shape == (None, 150, 1, 16)\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      # Convolutional-Transpose Layer #5\n",
        "      model.add(layers.Conv2DTranspose(1, (5, 1), strides=(1, 1), padding='same', use_bias=False)) # Conv-T\n",
        "      # Output\n",
        "      assert model.output_shape == (None, 150, 1, 1)\n",
        "\n",
        "      return model\n",
        "\n",
        "    else:\n",
        "      print('Set the number between 0 and 2 for GAN model')\n",
        "\n",
        "generator = make_generator_model()"
      ],
      "execution_count": 7,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yy6OkUcSjs2V"
      },
      "source": [
        "**The Discriminator**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MO8tavxIcmqS"
      },
      "source": [
        "def make_discriminator_model():\n",
        "    if GAN_model == 0: # DCGAN\n",
        "      model = tf.keras.Sequential()\n",
        "      # Convolutional Layer #1\n",
        "      model.add(layers.Conv2D(16, (5, 1), strides=(1, 1), padding='same', input_shape=[150, 1, 1])) # Conv\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #2\n",
        "      model.add(layers.Conv2D(32, (5, 1), strides=(2, 1), padding='same')) # Conv\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #3\n",
        "      model.add(layers.Conv2D(64, (5, 1), strides=(1, 1), padding='same')) # Conv\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #4\n",
        "      model.add(layers.Conv2D(128, (5, 1), strides=(3, 1), padding='same')) # Conv\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #5\n",
        "      model.add(layers.Conv2D(256, (5, 1), strides=(1, 1), padding='same')) # Conv\n",
        "      model.add(layers.BatchNormalization()) # BN\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # FC Layer + Output\n",
        "      model.add(layers.Flatten())\n",
        "      model.add(layers.Dense(1))\n",
        "      model.add(layers.Activation('sigmoid')) # Sigmoid\n",
        "      \n",
        "      return model\n",
        "\n",
        "    elif (GAN_model == 1) or (GAN_model == 2): # WGAN or LSGAN\n",
        "      model = tf.keras.Sequential()\n",
        "      # Convolutional Layer #1\n",
        "      model.add(layers.Conv2D(16, (5, 1), strides=(1, 1), padding='same', input_shape=[150, 1, 1])) # Conv\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #2\n",
        "      model.add(layers.Conv2D(32, (5, 1), strides=(2, 1), padding='same')) # Conv\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #3\n",
        "      model.add(layers.Conv2D(64, (5, 1), strides=(1, 1), padding='same')) # Conv\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #4\n",
        "      model.add(layers.Conv2D(128, (5, 1), strides=(3, 1), padding='same')) # Conv\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu\n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # Convolutional Layer #5\n",
        "      model.add(layers.Conv2D(256, (5, 1), strides=(1, 1), padding='same')) # Conv\n",
        "      model.add(layers.LeakyReLU()) # LeakyReLu \n",
        "      model.add(layers.Dropout(0.5)) # Dropout\n",
        "      # FC Layer + Output\n",
        "      model.add(layers.Flatten())\n",
        "      model.add(layers.Dense(1))    \n",
        "\n",
        "      return model\n",
        "\n",
        "    else:\n",
        "      print('Set the number between 0 and 2 for GAN model')\n",
        "\n",
        "discriminator = make_discriminator_model()"
      ],
      "execution_count": 8,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lfDCzHHsqCxy"
      },
      "source": [
        "**The Generator Loss**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SDRhUdTLcm1i"
      },
      "source": [
        "cross_entropy = tf.keras.losses.BinaryCrossentropy()\n",
        "\n",
        "if PBGAN == 0 : # PBGAN from original GANs\n",
        "\n",
        "  def gradient(x): # Function for calculating gradient\n",
        "      grad = []\n",
        "      for i in range(n_feature-1):\n",
        "        grad.append(x[:,i+1]-x[:,i])\n",
        "\n",
        "      return tf.convert_to_tensor(tf.transpose(grad))\n",
        "\n",
        "  def integral(y, x): # Function for calculating area\n",
        "      dx = (x[-1] - x[0]) / (int(x.shape[0]) - 1)\n",
        "      dx = tf.cast(dx,dtype=tf.float64)\n",
        "      return ((y[:,0] + y[:,-1])/2 + tf.reduce_sum(y[:,1:-1],axis=1)) * dx\n",
        "\n",
        "  def autocorr(x): # Function for calculating autocorrelation\n",
        "      y = x - np.mean(x)\n",
        "      norm = np.sum(y ** 2)\n",
        "      correlated = np.correlate(x, x, mode='full') / norm\n",
        "\n",
        "      return correlated[int(len(correlated)/2):]\n",
        "\n",
        "  def generator_loss(images, generated_images, fake_output, epoch):\n",
        "      if epoch>=10: # After epoch=10, use MSE loss with original loss (PBGAN)\n",
        "        result_area = result_up = result_down = cor_1 = cor_2 = cor_3 = 0.0 # Features for PBGAN\n",
        "        a = tf.constant(0, dtype=tf.float64)\n",
        "        b = tf.constant(n_feature-1, dtype=tf.float64)\n",
        "        x = tf.linspace(a, b, n_feature)\n",
        "        \n",
        "        temp_real = tf.cast(tf.reshape(images, [len(images), n_feature]), dtype=tf.float64) # Real data\n",
        "        temp_gen = tf.cast(tf.reshape(generated_images, [len(generated_images), n_feature]), dtype=tf.float64) # Fake data\n",
        "          \n",
        "        result_area = tf.reduce_mean(tf.pow(tf.subtract(integral(temp_real, x), integral(temp_gen, x)),2)) # MSE from area feature\n",
        "\n",
        "        r_sys_loc = tf.argmax(temp_real,axis=1) # Peak location in real data\n",
        "        g_sys_loc = tf.argmax(temp_gen,axis=1) # Peak location in fake data\n",
        "        grad_r = gradient(temp_real) # Gradient information for real data\n",
        "        grad_g = gradient(temp_gen) # Gradient information for fake data\n",
        "\n",
        "        r_up = []\n",
        "        r_dw = []\n",
        "        g_up = []\n",
        "        g_dw = []\n",
        "        cor_1_r_tmp = []       \n",
        "        cor_2_r_tmp = []\n",
        "        cor_3_r_tmp = []\n",
        "        cor_1_g_tmp = []\n",
        "        cor_2_g_tmp = []\n",
        "        cor_3_g_tmp = []\n",
        "\n",
        "        for i in range(len(r_sys_loc)):\n",
        "          \n",
        "          grad_r_tmp = tf.math.abs(grad_r[i, r_sys_loc[i] - 10:r_sys_loc[i]+ 15])\n",
        "          grad_g_tmp = tf.math.abs(grad_g[i, g_sys_loc[i] - 10:g_sys_loc[i]+ 15])\n",
        "\n",
        "          r_up.append(tf.cast(tf.math.reduce_max(grad_r_tmp[:][:10]), dtype=tf.float64)) # Max upward slope near peak (real_data)\n",
        "          r_dw.append(tf.cast(tf.math.reduce_max(grad_r_tmp[:][10:]), dtype=tf.float64)) # Max downward slope near peak (real_data)\n",
        "          g_up.append(tf.cast(tf.math.reduce_max(grad_g_tmp[:][:10]), dtype=tf.float64)) # Max upward slope near peak (fake_data)\n",
        "          g_dw.append(tf.cast(tf.math.reduce_max(grad_g_tmp[:][10:]), dtype=tf.float64)) # Max downward slope near peak (fake_data)\n",
        "\n",
        "          ac_r = autocorr(temp_real[i])\n",
        "          ac_g = autocorr(temp_gen[i])        \n",
        "          cor_1_r_tmp.append(ac_r[int((n_feature-1)/4)]) # Autocorrelation at 1/4 lag (real_data)\n",
        "          cor_2_r_tmp.append(ac_r[int(2*(n_feature-1)/4)]) # Autocorrelation at 2/4 lag (real_data)\n",
        "          cor_3_r_tmp.append(ac_r[int(3*(n_feature-1)/4)]) # Autocorrelation at 3/4 lag (real_data)\n",
        "          cor_1_g_tmp.append(ac_g[int((n_feature-1)/4)]) # Autocorrelation at 1/4 lag (fake_data)\n",
        "          cor_2_g_tmp.append(ac_g[int(2*(n_feature-1)/4)]) # Autocorrelation at 2/4 lag (fake_data)\n",
        "          cor_3_g_tmp.append(ac_g[int(3*(n_feature-1)/4)]) # Autocorrelation at 3/4 lag (fake_data)\n",
        "\n",
        "        result_up = tf.reduce_mean(tf.pow(tf.subtract(r_up, g_up),2)) # MSE from max upward slope feature\n",
        "        result_down = tf.reduce_mean(tf.pow(tf.subtract(r_dw, g_dw),2)) # MSE from max downward slope feature\n",
        "\n",
        "        cor_1 = tf.reduce_mean(tf.pow(tf.subtract(cor_1_r_tmp, cor_1_g_tmp),2)) # MSE from autocorrelation (1/4 lag) feature\n",
        "        cor_2 = tf.reduce_mean(tf.pow(tf.subtract(cor_2_r_tmp, cor_2_g_tmp),2)) # MSE from autocorrelation (2/4 lag) feature\n",
        "        cor_3 = tf.reduce_mean(tf.pow(tf.subtract(cor_3_r_tmp, cor_3_g_tmp),2)) # MSE from autocorrelation (3/4 lag) feature\n",
        "\n",
        "        r_f, r_psd = signal.welch(temp_real, fs=100) # Power spectral density (PSD) in real data\n",
        "        g_f, g_psd = signal.welch(temp_gen, fs=100) # PSD in fake data\n",
        "\n",
        "        psd = tf.reduce_mean(tf.pow(tf.subtract(np.trapz(r_psd,axis=1), np.trapz(g_psd,axis=1)),2)) # MSE from area of PSD feature\n",
        "        MSE_loss = 0.49*result_area + 0.58*result_up + 1*result_down + 0.39*cor_1 + 0.46*cor_2 + 0.21*cor_3 + 0.95*psd # Different weights for all features in proposed loss\n",
        "        # MSE_loss = result_area + result_up + result_down + cor_1 + cor_2 + cor_3 + psd  # Same weights for all features in proposed loss\n",
        "\n",
        "        if GAN_model == 0: # PBGAN-DC\n",
        "          original_loss = tf.cast(cross_entropy(tf.ones_like(fake_output), fake_output), dtype=tf.float64) # cross entropy with fake\n",
        "\n",
        "        elif GAN_model == 1: # PBGAN-W\n",
        "          original_loss = tf.cast(-tf.reduce_mean(fake_output), dtype=tf.float64) # Loss for WGAN\n",
        "\n",
        "        elif GAN_model == 2: # PBGAN-LS\n",
        "          original_loss = tf.cast(tf.reduce_mean(tf.nn.l2_loss(fake_output - tf.ones_like(fake_output))) / 2, dtype=tf.float64) # L2 norm with fake\n",
        "\n",
        "        else:    \n",
        "          print('Set the number between 0 and 2 for GAN model')\n",
        "\n",
        "        return original_loss + MSE_loss \n",
        "\n",
        "      else: # Before epoch 10, use only original loss function for better synthetic data\n",
        "\n",
        "        if GAN_model == 0: # DCGAN\n",
        "          original_loss = tf.cast(cross_entropy(tf.ones_like(fake_output), fake_output), dtype=tf.float64) # cross entropy with fake\n",
        "\n",
        "        elif GAN_model == 1: # WGAN\n",
        "          original_loss = tf.cast(-tf.reduce_mean(fake_output), dtype=tf.float64) # Loss for WGAN\n",
        "\n",
        "        elif GAN_model == 2: # LSGAN\n",
        "          original_loss = tf.cast(tf.reduce_mean(tf.nn.l2_loss(fake_output - tf.ones_like(fake_output))) / 2, dtype=tf.float64) # L2 norm with fake\n",
        "\n",
        "        else:    \n",
        "          print('Set the number between 0 and 2 for GAN model')\n",
        "\n",
        "        return original_loss\n",
        "\n",
        "elif PBGAN == 1: # Original GANs\n",
        "  def generator_loss(images, generated_images, fake_output, epoch):\n",
        "\n",
        "    if GAN_model == 0: # DCGAN\n",
        "      original_loss = tf.cast(cross_entropy(tf.ones_like(fake_output), fake_output), dtype=tf.float64) # cross entropy with fake\n",
        "\n",
        "    elif GAN_model == 1: # WGAN\n",
        "      original_loss = tf.cast(-tf.reduce_mean(fake_output), dtype=tf.float64) # Loss for WGAN\n",
        "\n",
        "    elif GAN_model == 2: # LSGAN\n",
        "      original_loss = tf.cast(tf.reduce_mean(tf.nn.l2_loss(fake_output - tf.ones_like(fake_output))) / 2, dtype=tf.float64) # L2 norm with fake\n",
        "\n",
        "    else:    \n",
        "      print('Set the number between 0 and 2 for GAN model')\n",
        "\n",
        "    return original_loss\n",
        "\n",
        "else: \n",
        "  print('Set the number between 0 and 1 for PBGAN')"
      ],
      "execution_count": 9,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rRXvh4bCqJNH"
      },
      "source": [
        "**The Discriminator Loss**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "d_Pm_AAbqM95"
      },
      "source": [
        "if GAN_model == 0: # DCGAN\n",
        "  def discriminator_loss(real_output, fake_output):    \n",
        "      real_loss = cross_entropy(tf.ones_like(real_output), real_output) # Cross entropy with real\n",
        "      fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output) # Cross entropy with fake\n",
        "\n",
        "      total_loss = real_loss + fake_loss\n",
        "\n",
        "      return total_loss\n",
        "\n",
        "elif GAN_model == 1: # WGAN\n",
        "  def discriminator_loss(real_data, fake_data): \n",
        "    # Calculate the gradient penalty term \n",
        "    LAMBDA = 10\n",
        "    alpha = tf.cast(tf.random.uniform([BATCH_SIZE, 150, 1, 1], 0.,1.), dtype=tf.float64)\n",
        "    real_data = tf.cast(real_data, dtype=tf.float64)\n",
        "    fake_data = tf.cast(fake_data, dtype=tf.float64)    \n",
        "    interpolates = alpha * real_data + (1-alpha) * fake_data\n",
        "    \n",
        "    with tf.GradientTape() as gp_tape:\n",
        "      gp_tape.watch(interpolates)\n",
        "      pred = discriminator(interpolates)\n",
        "\n",
        "    gradients = gp_tape.gradient(pred, [interpolates])[0]\n",
        "    \n",
        "    slopes = tf.sqrt(tf.reduce_sum(tf.square(gradients), axis=[1,2,3]))\n",
        "\n",
        "    gradient_penalty = tf.reduce_mean((slopes-1)**2) # Gradient penalty term\n",
        "\n",
        "    real_output = discriminator(real_data, training=True)\n",
        "    fake_output = discriminator(fake_data, training=True)\n",
        "    \n",
        "    wasserstein_dist = tf.cast(tf.reduce_mean(fake_output) - tf.reduce_mean(real_output), dtype=tf.float64) # Loss for WGAN \n",
        "\n",
        "    return wasserstein_dist + LAMBDA*gradient_penalty # Loss with gradient penalty term\n",
        "\n",
        "elif GAN_model == 2: # LSGAN\n",
        "  def discriminator_loss(real_output, fake_output):    \n",
        "       \n",
        "    loss_real = tf.reduce_mean(tf.nn.l2_loss(real_output - tf.ones_like(real_output))) # L2 norm with real\n",
        "    loss_fake = tf.reduce_mean(tf.nn.l2_loss(fake_output - tf.zeros_like(fake_output))) # L2 norm with fake\n",
        "\n",
        "    loss = (loss_real + loss_fake) / 2\n",
        "\n",
        "    return loss\n",
        "\n",
        "else:    \n",
        "    print('Set the number between 0 and 2 for GAN model')\n"
      ],
      "execution_count": 10,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4o6Tv2Aj0yV1"
      },
      "source": [
        "**Optimizers**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CQAblR-qAkxR"
      },
      "source": [
        "if GAN_model == 0: # DCGAN\n",
        "  generator_optimizer = tf.keras.optimizers.Adam(1e-3)\n",
        "  discriminator_optimizer = tf.keras.optimizers.Adam(1e-3)\n",
        "\n",
        "elif GAN_model == 1: # WGAN\n",
        "  generator_optimizer = tf.keras.optimizers.Adam(1e-4, beta_1 = 0, beta_2 = 0.9) \n",
        "  discriminator_optimizer = tf.keras.optimizers.Adam(1e-4, beta_1 = 0, beta_2 = 0.9)\n",
        "\n",
        "elif GAN_model == 2: # LSGAN\n",
        "  generator_optimizer = tf.keras.optimizers.RMSprop(1e-3)\n",
        "  discriminator_optimizer = tf.keras.optimizers.RMSprop(1e-3)\n",
        "else:    \n",
        "  print('Set the number between 0 and 2 for GAN model')\n"
      ],
      "execution_count": 11,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9Zy_AdSD1R1h"
      },
      "source": [
        "**Training Set Up**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YmZoRLIgAkz8"
      },
      "source": [
        "EPOCHS = 100\n",
        "noise_dim = 200 # Size of input for generator\n",
        "num_examples_to_generate = 10 # Number of generated synthetic data in each mini-batch"
      ],
      "execution_count": 12,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "q5w4-sUmAk21"
      },
      "source": [
        "if (GAN_model == 0) or (GAN_model == 2): # DCGAN or LSGAN\n",
        "  def train_step(images,label,epoch):  \n",
        "      noise = tf.random.normal([BATCH_SIZE, noise_dim]) # Input for generator \n",
        "\n",
        "      with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:\n",
        "        generated_images = generator(noise, training=True) # Synthetic data\n",
        "        generated_images = tf.reshape(generated_images, [len(generated_images),150]) \n",
        "        mean_generate = tf.reshape(tf.math.reduce_mean(generated_images,axis=1),[len(generated_images),1])\n",
        "        std_generate = tf.reshape(tf.math.reduce_std(generated_images,axis=1),[len(generated_images),1])\n",
        "\n",
        "        generated_images = tf.divide(tf.subtract(generated_images, mean_generate), std_generate) # Standardization\n",
        "        generated_images = tf.reshape(generated_images, [len(generated_images),150,1,1])\n",
        "        real_output = discriminator(images, training=True) \n",
        "        fake_output = discriminator(generated_images, training=True) \n",
        "\n",
        "        gen_loss = generator_loss(images, generated_images, fake_output, epoch) # Loss from generator\n",
        "        disc_loss = discriminator_loss(real_output, fake_output) # Loss from discriminator\n",
        "\n",
        "      gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n",
        "      gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n",
        "\n",
        "      generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) # Optimizer for generator \n",
        "      discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) # Optimizer for discriminator\n",
        "\n",
        "\n",
        "elif GAN_model == 1: # WGAN\n",
        "  def train_step(images,label,epoch):  \n",
        "\n",
        "      for _ in range(5): # Train discriminator 5 times more than generator\n",
        "\n",
        "        noise = tf.random.normal([BATCH_SIZE, noise_dim]) # Input for generator\n",
        "    \n",
        "        with tf.GradientTape() as disc_tape:\n",
        "          generated_images = generator(noise, training=True) # Synthetic data          \n",
        "          generated_images = tf.reshape(generated_images, [len(generated_images),150])\n",
        "          mean_generate = tf.reshape(tf.math.reduce_mean(generated_images,axis=1),[len(generated_images),1])\n",
        "          std_generate = tf.reshape(tf.math.reduce_std(generated_images,axis=1),[len(generated_images),1])\n",
        "\n",
        "          generated_images = tf.divide(tf.subtract(generated_images, mean_generate), std_generate) # Standardization\n",
        "          generated_images = tf.reshape(generated_images, [len(generated_images),150,1,1])\n",
        "\n",
        "          disc_loss = discriminator_loss(images, generated_images) # Loss from discriminator\n",
        "          gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)\n",
        "          discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) # Optimizer for discriminator     \n",
        "\n",
        "\n",
        "      noise = tf.random.normal([BATCH_SIZE, noise_dim]) # Input for generator\n",
        "\n",
        "      with tf.GradientTape() as gen_tape:        \n",
        "        generated_images = generator(noise, training=True) # Synthetic data            \n",
        "        generated_images = tf.reshape(generated_images, [len(generated_images),150])\n",
        "        mean_generate = tf.reshape(tf.math.reduce_mean(generated_images,axis=1),[len(generated_images),1])\n",
        "        std_generate = tf.reshape(tf.math.reduce_std(generated_images,axis=1),[len(generated_images),1])\n",
        "\n",
        "        generated_images = tf.divide(tf.subtract(generated_images, mean_generate), std_generate) # Standardization\n",
        "        generated_images = tf.reshape(generated_images, [len(generated_images),150,1,1])\n",
        "    \n",
        "        fake_output = discriminator(generated_images, training=True)\n",
        "        gen_loss = generator_loss(images, generated_images, fake_output, epoch) # Loss from generator\n",
        "        gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)\n",
        "        generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) # Optimizer for generator\n",
        "\n",
        "\n"
      ],
      "execution_count": 13,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zDOzolrtAk52",
        "outputId": "508288aa-140c-49d9-c94a-09c055eac8e8",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "def train(dataset, label, epochs): # Train during epoch\n",
        "\n",
        "  for epoch in range(epochs): \n",
        "    for image_batch, true_label in zip(train_dataset, labels):           \n",
        "      \n",
        "      _ = train_step(image_batch, true_label,epoch)\n",
        "\n",
        "\n",
        "print('Learning Started!')\n",
        "start_time = time.time()\n",
        "\n",
        "_ = train(train_dataset, labels, EPOCHS) # Train start!\n",
        "\n",
        "print('Learning Finished!')\n",
        "print('Building time for GAN: {:.2f} seconds'.format(time.time()-start_time))    "
      ],
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Learning Started!\n",
            "Learning Finished!\n",
            "Building time for GAN: 200.08 seconds\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "aOcoUzjZAk8m",
        "outputId": "84574530-d85b-4b14-af94-0ab6fb301a15",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 298
        }
      },
      "source": [
        "noise = tf.random.normal([120, noise_dim]) # Input for generator to build target's synthetic data\n",
        "\n",
        "syn_data = generator(noise, training=False) # Target's synthetic data\n",
        "syn_data = np.reshape(syn_data,[120,150])\n",
        "mean_syn = np.reshape(np.mean(syn_data,axis=1),[len(syn_data),1])\n",
        "std_syn = np.reshape(np.std(syn_data,axis=1),[len(syn_data),1])\n",
        "\n",
        "syn_data = np.divide(syn_data - mean_syn, std_syn) # Standardization\n",
        "\n",
        "fig2 = plt.figure()\n",
        "ax = plt.subplot(111)\n",
        "ax.plot(syn_data[3,:])\n",
        "plt.title('Example of synthetic data')"
      ],
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "Text(0.5, 1.0, 'Example of synthetic data')"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 20
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ITdsHcaqPT68"
      },
      "source": [
        "fig.savefig('Original data sample.png')\n",
        "fig2.savefig('Synthetic data sample.png')"
      ],
      "execution_count": 21,
      "outputs": []
    }
  ]
}